#pragma once
#include "nnse_support.h"

namespace nnse
{
	template <class T, class U> public ref class solver
	{
		// Constructor and Destructor
			public: solver(problem<T, U>^ p, System::String^ t);
			public: virtual ~solver();
		// Member Variables and their Accessor Functions
			// parent - the problem to which this solver belongs
				private: problem<T, U>^ parent;
				public: problem<T, U>^ get_parent_problem();
			// title - a title for this solver
				private: System::String^ title;
				public: System::String^ get_title();
				protected: void put_title(System::String^ t);
			// training_output_vector - an array of outputs from the training process; these should gradually come to match the targets
				private: array<U>^ training_output_vector;
				public: U get_training_output_value(unsigned int o_n);
				public: void put_training_output_value(unsigned int o_n, U v);
			// testing_output_vector - an array of outputs from the testing process; these should now match the targets
				private: array<U>^ testing_output_vector;
				public: U get_testing_output_value(unsigned int o_n);
				public: void put_testing_output_value(unsigned int o_n, U v);
			// the following functions use training_flag to determine which of the above get_***_output_value or put_***_output_value functions to call
				public: U get_output_value(unsigned int el);
				public: void put_output_value(unsigned int el, U v);
			// number_of_MLPs - the number of MLPs in this solver
				private: unsigned int number_of_MLPs;
				public: unsigned int get_number_of_MLPs();
				protected: void put_number_of_MLPs(unsigned int n_MLPs);
			// MLPs - the MLPs in this solver
				private: array<MLP<T, U>^>^ MLPs;
				public: MLP<T, U>% get_MLP(unsigned int MLP_n);
				protected: void put_MLPs();
			// solver_form - the solver's user interface
				protected: SimpleGUI::Solver<T, U>^ solver_form;
				protected: SimpleGUI::Input<T, U>^ input_form;
				protected: SimpleGUI::Output<T, U>^ output_form;
				protected: SimpleGUI::Target<T, U>^ target_form;
		// Worker Functions
			public: virtual void load(System::String^ f_s);
			public: virtual void save(System::String^ f_s);
			public: virtual void training(unsigned int MLP_n);
			public: virtual void testing(unsigned int MLP_n);
			protected: virtual void allocate_storage();
			public: virtual unsigned int normalise_output_vector();
			protected: virtual void get_min_output_val(U& min_val, unsigned int& min_el);
			protected: virtual void get_max_output_val(U& max_val, unsigned int& max_el);
		// GUI Functions
			public: void display_solver_form(System::String^ progress_text, unsigned int progress_min, unsigned int progress_val, unsigned int progress_max);
			public: void show_vectors();
			public: void display_input_form();
			public: void display_output_form();
			public: void display_target_form();
			public: void invalidate_solver_form();
			public: void invalidate_input_form();
			public: void invalidate_output_form();
			public: void invalidate_target_form();
			public: void display_MLPs();
		// Serialisation Functions
			public: virtual void read(std::wifstream& in);
			public: virtual void write(std::wofstream& out);
			public: virtual void display(std::wofstream& out);
	};
	template <class T, class U> std::wifstream& operator>>(std::wifstream& in, solver<T, U>% s);
	template <class T, class U> std::wofstream& operator<<(std::wofstream& out, solver<T, U>% s);
}
